﻿// Decompiled with JetBrains decompiler
// Type: Netick.RemoteInterpolation
// Assembly: Netick, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 6726ECA1-C773-4CF7-8952-E81B30D93B52
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.dll
// XML documentation location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.xml

using System;
using System.Collections.Generic;

#nullable disable
namespace Netick;

/// <summary>
/// A class representing the remote interpolation of this instance <see cref="T:Netick.NetickEngine" />. This should be used whenever you want to use Remote Timeline on the client.
/// </summary>
public sealed class RemoteInterpolation : Interpolation
{
  internal RingBuffer<Tick> _buffer;
  internal RingBuffer<NetickAllocator> _snapshots;
  internal Queue<NetickAllocator> _snapshotsPool;
  internal List<NetickAllocator> _snapshotsList;
  internal NetickAllocator _lastState;
  private Tick _from = Tick.InvalidTick;
  private Tick _to = Tick.InvalidTick;
  private float _bufferAsTime;
  private float _alpha;
  private float _currentLerpTime;
  private int _ticks;
  private double _lastTime;
  private DoubleStats _packetTimeFast;
  private DoubleStats _packetTimeSlow;
  private DoubleStats _curPacketTime;

  /// <summary>Interpolation time in seconds.</summary>
  internal float CurrentBufferTime
  {
    get
    {
      return this._bufferAsTime + (float) (this.Engine._timer.Time - this._lastTime) - this._currentLerpTime;
    }
  }

  public override Tick From => this._from;

  public override Tick To => this._to;

  public override float Alpha => this._alpha;

  public override bool HasSnapshots => this.FromSnapshot != null && this.ToSnapshot != null;

  public float BufferTime => this.CurrentBufferTime;

  /// <summary>Time in seconds.</summary>
  public override float Time
  {
    get
    {
      return NetickUtils.Lerp((float) this._from.Value, (float) this._to.Value, this._alpha) * this.Engine.FixedDeltaTime;
    }
  }

  internal RemoteInterpolation(NetickEngine engine)
    : base(engine)
  {
    int interpolationSnapshots = engine._config.MaxInterpolationSnapshots;
    this._packetTimeFast = new DoubleStats(interpolationSnapshots);
    this._packetTimeSlow = new DoubleStats(interpolationSnapshots * 3);
    this._curPacketTime = this._packetTimeFast;
    int num = interpolationSnapshots * 2;
    this._buffer = new RingBuffer<Tick>(num);
    this._snapshots = new RingBuffer<NetickAllocator>(num);
    this._snapshotsPool = new Queue<NetickAllocator>(num);
    this._snapshotsList = new List<NetickAllocator>(num);
    if (!engine.IsClient)
      return;
    for (int index = 0; index < num; ++index)
    {
      NetickAllocator buffer = new NetickAllocator((long) engine._config.StateAllocatorBlockSize);
      this._snapshotsList.Add(buffer);
      this._snapshotsPool.Enqueue(buffer);
      this.Engine._cliSim.StateAllocator.Link(buffer);
    }
  }

  public void Reset()
  {
    this._from = Tick.InvalidTick;
    this._to = Tick.InvalidTick;
    this._lastTime = 0.0;
    this._lastState = (NetickAllocator) null;
    this._bufferAsTime = 0.0f;
    this._ticks = 0;
    this._alpha = 0.0f;
    this._currentLerpTime = 0.0f;
    this._packetTimeFast.Reset();
    this._packetTimeSlow.Reset();
    this._buffer.FastClear();
    this._snapshots.FastClear();
    this._snapshotsPool.Clear();
    if (this.Engine.IsServer)
      return;
    for (int index = 0; index < this._snapshotsList.Count; ++index)
      this._snapshotsPool.Enqueue(this._snapshotsList[index]);
    if (!this.Engine.IsClient || this.Engine._adaptiveReplicator == null)
      return;
    for (int index = 0; index < this.Engine._adaptiveReplicator.SAPEntities.Count; ++index)
      this.Engine._adaptiveReplicator.SAPEntities[index].AdaptiveRemoteInterpolation?.Reset();
  }

  internal void Update(int serverDivisor)
  {
    this.FromSnapshot = (NetickAllocator) null;
    this.ToSnapshot = (NetickAllocator) null;
    double fixedDeltaTime = (double) this.Engine.FixedDeltaTime;
    double val1 = fixedDeltaTime * (double) serverDivisor;
    double num1 = Math.Max(val1, this._curPacketTime.Average) + Math.Max(0.0035000001080334187, this._curPacketTime.StdDeviation * 4.0 - 0.0035) + (double) NetickUtils.Clamp01(this.Engine.Config.ExtraInterpolationDelay) * val1;
    double num2;
    if (this.Engine._config.BurstBuffering)
    {
      double num3 = (double) (1 + Math.Min(6, this.Engine.Client.ConnectedServer.InLossCalculator.LossBurst)) * val1;
      num2 = num3 >= num1 ? num3 : num1;
      this._curPacketTime = (double) this.Engine.Client.ConnectedServer.InLossCalculator.LossRate >= 0.1 ? this._packetTimeSlow : this._packetTimeFast;
    }
    else
      num2 = num1;
    if (!this.Engine._cliSim.IsLastTickFull)
      this._lastTime = this.Engine._timer.Time;
    else if (this._buffer.Count < 2)
    {
      this._ticks = 0;
    }
    else
    {
      if (this._ticks < 2 * serverDivisor)
        return;
      this._from = this._buffer[0];
      this._to = this._buffer[1];
      float num4 = (float) (this.To - this.From) * this.Engine.FixedDeltaTime;
      double num5 = (double) NetickUtils.Clamp01(this.CurrentBufferTime - (float) fixedDeltaTime) - num2;
      double num6 = 0.001500000013038516;
      float num7 = 1f;
      if (num5 > num6)
        num7 = this._curPacketTime != this._packetTimeFast ? (num5 <= val1 * 2.0 ? 1.01f : NetickUtils.Lerp(1.1f, 1.5f, NetickUtils.InverseLerp((float) val1 * 2f, (float) val1 * 10f, (float) num5))) : (num5 <= val1 * 2.0 ? 1.01f : NetickUtils.Lerp(1.3f, 1.8f, NetickUtils.InverseLerp((float) val1 * 2f, (float) val1 * 10f, (float) num5)));
      else if (num5 < -num6)
        num7 = num5 < -val1 * 3.0 ? 0.89f : 0.99f;
      if ((double) this._currentLerpTime < (double) num4)
      {
        this._currentLerpTime += this.Engine._timer._deltaTime * num7;
        this._alpha = this._currentLerpTime / num4;
      }
      if ((double) this._currentLerpTime > (double) num4)
      {
        for (; (double) this._alpha > 1.0; --this._alpha)
        {
          if (this._buffer.Count == 0)
          {
            this._currentLerpTime = 0.0f;
            break;
          }
          this.Dequeue();
          this._bufferAsTime -= num4;
          this._currentLerpTime -= num4;
        }
        if (this._buffer.Count < 2)
        {
          this._bufferAsTime = 0.0f;
          this._currentLerpTime = 0.0f;
        }
      }
      this._alpha = this._currentLerpTime / num4;
      if (this._buffer.Count >= 2)
      {
        this._from = this._buffer[0];
        this._to = this._buffer[1];
        this.FromSnapshot = this._snapshots[0];
        this.ToSnapshot = this._snapshots[1];
      }
      else
      {
        this._ticks = 0;
        if (!this.Engine.IsClient || this.Engine._adaptiveReplicator == null)
          return;
        for (int index = 0; index < this.Engine._adaptiveReplicator.SAPEntities.Count; ++index)
          this.Engine._adaptiveReplicator.SAPEntities[index].AdaptiveRemoteInterpolation.Reset();
      }
    }
  }

  internal void AddSnapshot(Tick newTick, NetickAllocator authState)
  {
    double time = this.Engine._timer.Time;
    double num1 = this.Engine._timer.Time - this._lastTime;
    if (this._lastTime != 0.0)
    {
      this._packetTimeFast.Update(num1);
      this._packetTimeSlow.Update(num1);
    }
    this._lastTime = time;
    if (this._buffer.Count == 0)
    {
      NetickAllocator netickAllocator = this._snapshotsPool.Dequeue();
      authState.CopyTo(netickAllocator);
      this.Enqueue(newTick, netickAllocator);
    }
    else
    {
      int num2 = newTick - this._buffer.Last;
      NetickAllocator netickAllocator = this._snapshotsPool.Dequeue();
      authState.CopyTo(netickAllocator);
      this.Enqueue(newTick, netickAllocator);
      this._bufferAsTime += (float) num2 * this.Engine.FixedDeltaTime;
      ++this._ticks;
      if (!this._buffer.IsFull)
        return;
      this.Reset();
    }
  }

  private void Dequeue()
  {
    this._snapshotsPool.Enqueue(this._snapshots[0]);
    this._buffer.RemoveFromStart(1);
    this._snapshots.RemoveFromStart(1);
  }

  private void Enqueue(Tick newTick, NetickAllocator snapshot)
  {
    this._lastState = snapshot;
    this._buffer.Add(newTick);
    this._snapshots.Add(snapshot);
  }
}
